package DGWeb.Servlets;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.SingleThreadModel;

import org.apache.tomcat.dbcp.dbcp.BasicDataSource;
import org.joda.time.*;

import com.google.gson.Gson;

import DGWeb.AppConstants;
import DGWeb.Model.*;

/**
 * Servlet implementation class getMessages
 */
@SuppressWarnings("deprecation")
@WebServlet("/getMessages")
public class getMessages extends HttpServlet implements SingleThreadModel {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public getMessages() {
        super();
        // TODO Auto-generated constructor stub
    }
    
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{
		HttpSession userSession = request.getSession();
		String currentuser = (String) userSession.getAttribute("Username"); // get the user from the session
		
		
		if ( currentuser == null) // if the someone tries to go directly to main.html 
			 return;		
		// For Parameter
		InputStreamReader isr = new InputStreamReader(request.getInputStream());
		BufferedReader br = new BufferedReader(isr);
		StringBuilder jsonFileContent = new StringBuilder();
		jsonFileContent.append( br.readLine());
		//Pasre data - we need in here only the Type
		Gson gson = new Gson();
		User userProfile = gson.fromJson(jsonFileContent.toString(),User.class );
		model_MessageInsert CurrMsg = gson.fromJson(jsonFileContent.toString(),model_MessageInsert.class );
		// if the parameter is empty

		
		Context context = null;
		BasicDataSource ds = null;
		Connection conn = null;
		Statement stmt = null;
		PrintWriter writer =null;
		ResultSet res = null;
		SendMessageToClient JuspPostedmsg = null;
		Collection<SendMessageToClient> MessagesResult =new ArrayList<SendMessageToClient>();//  (Collection<SendMessageToClient>)getServletContext().getAttribute(AppConstants.MESSAGES_TABLE);
		
		try 
		{
			context = new InitialContext();
			ds = (BasicDataSource)context.lookup(AppConstants.DB_DATASOURCE);
			conn = ds.getConnection();
			//create Customers table
			stmt = conn.createStatement();			
			Collection<SendMessageToClient> temp = new ArrayList<SendMessageToClient>();
			String exec = null;
			String Followdisable; // flag for each message to disable or not the follow button
			String UnFollowDisable;
			int types = Integer.parseInt(CurrMsg.getType());
			
			if ((types%10) == 3 && types != 63) // mode the bring the users message to the begining
			{
				String LastMessageId= (String) userSession.getAttribute(AppConstants.MESSAGES_ID); // bring last message id that we saved in message insert
				exec = AppConstants.GET_MESSAGE_LAST.replaceAll("//", LastMessageId); // bring last message
				res = stmt.executeQuery(exec);
				if ( res.next())
				{
					String id = res.getString(AppConstants.MESSAGES_ID);
					String Username= res.getString(AppConstants.MESSAGES_WROTE);
					String Nickname= res.getString(AppConstants.MESSAGES_WROTE_NICKNAME);
					String Text = res.getString(AppConstants.MESSAGES_TEXT);
					Text = AbstractGet.chkUsersTopics(Text);
					String Time = res.getString(AppConstants.MESSAGES_TIME);
					Time = calcTime(Time);
					int Comments =0;
					Followdisable = String.valueOf(false); // disbale Follow - if it the same user
					UnFollowDisable = String.valueOf(false);// disable UnFollow
					JuspPostedmsg = new SendMessageToClient(Integer.parseInt(id),Username,Text,Time,Followdisable,Nickname,UnFollowDisable,Comments,"");
				}
			}
			
			
			if ( (types < 10 && types == 1)|| types >10 && types == 13 ){ // Bring New Messages
				 exec = AppConstants.GET_MESSAGES_SELECT.replaceAll("//", currentuser);
				 types = 1;
			}else if ( (types < 10 && types == 2) || types > 10 && types == 23 ){ //Bring only messages from people that I follow
				exec = AppConstants.GET_MESSAGES_SELECT_FROM_WHO_I_FOLLOW.replaceAll("//", currentuser);
				types = 2;
			}else if (types == 4){
				exec = AppConstants.GET_MESSAGES_SELECT_OF_ME.replaceAll("//", userProfile.getUsername());
				types = 4;
			}else if (types == 5 ){ // for topics
				String TopicText = (String) userSession.getAttribute("Topic");
				if(TopicText == null)
					return;
				exec = AppConstants.GET_TOPICS_FROM_MESSAGES_TABLE.replaceFirst("//", TopicText);
				types = 5;
			}
			else if (types == 6 || (types/10) == 6 ){ // Current User messages
				types = 6;
				exec = AppConstants.GET_MESSAGE_FROM_USER.replaceFirst("//", currentuser);
			}
			
			res = stmt.executeQuery(exec);
			String Nickname=null;

			while(res.next()  )//for ( int i =0;  i < AppConstants.MAX_MESSAGES ; i++ ) // nubmer of message to be sent  TBD
			{
				String id = res.getString(AppConstants.MESSAGES_ID);
				String Username= res.getString(AppConstants.MESSAGES_WROTE);
				Nickname= res.getString(AppConstants.MESSAGES_WROTE_NICKNAME);
				String Text = res.getString(AppConstants.MESSAGES_TEXT);
				Text = AbstractGet.chkUsersTopics(Text);
				String Time = res.getString(AppConstants.MESSAGES_TIME);
				String DateTime = Time;
				Time = calcTime(Time);
				int Comments = getCommentsNumber( Integer.parseInt(id));
				
				if ( Username.equals( currentuser)  )
				{
					Followdisable = String.valueOf(false); // disbale Follow - if it the same user
					UnFollowDisable = String.valueOf(false);// disable UnFollow
				}
				else
				{
					if(CheckIfWeFollowed(currentuser,Username) )
					{ // if it is true - user allready followed 
						Followdisable = String.valueOf(false); // Disable Follow
						UnFollowDisable = String.valueOf(true);// Enable UnFollow
					}
					else
					{
						Followdisable = String.valueOf(true); // enable Follow;
						UnFollowDisable = String.valueOf(false);// disable UnFollow
					}
				}

					
				SendMessageToClient msg = new SendMessageToClient(Integer.parseInt(id),Username,Text,Time,Followdisable,Nickname,UnFollowDisable,Comments,DateTime);
				temp.add(msg);
    			
			}
			// only for topic and current user message - we  want to show by Desc
			if( types == 5 || types == 6) 
			{
				Object[] FinalList = temp.toArray();
				for( int i =0 ; i < AppConstants.MAX_MESSAGES ; i++)
				{
					if ( i == temp.size())
						break;
					MessagesResult.add((SendMessageToClient)FinalList[i]);
				}
				
				
			}	
			// if types==4, then we asked to see only the messages+comments that were posted by the user who we are watching in his profile (and show it in his profile page). 
			// so we need to sort them by DATE in a descending order, and show the last 10.
			else if(types==4)
			{
				List<SendMessageToClient> comments_list = getCommentsOfUser(userProfile.getNickname());
				List<SendMessageToClient> messages_and_comments_list = new ArrayList<SendMessageToClient>(temp);
				messages_and_comments_list.addAll(comments_list);
				SortByDate(messages_and_comments_list);
				if(messages_and_comments_list.size() > 10){
					MessagesResult = messages_and_comments_list.subList(0, 10);
				}
				else MessagesResult = messages_and_comments_list;
			}
			else
			{// sort by popularity - in main page
				//add all other messages
				// add to the begining the message that the user has posted
				if ( JuspPostedmsg != null)
					MessagesResult.add(JuspPostedmsg);
				
				//add all other messages
				MessagesResult.addAll(SortByPopularity(temp));
			}
			//MessagesResult = temp;
			gson = new Gson();
			//convert from customers collection to json
			String MessageJsonResult = gson.toJson(MessagesResult, AppConstants.MESSAGES_COLLECTION);
			writer = response.getWriter();
			writer.println(MessageJsonResult);
			
			//release all 
			res.close();
			MessagesResult.clear();
			conn.commit();
			writer.close();
			stmt.close();
			conn.close();
			context.close();
			
			
		} 
		catch (NamingException |SQLException | ParseException e) {
			
			e.printStackTrace();

			if  (writer != null )
				writer.close();
			try {
				if (res != null )
					res.close();
				if (stmt != null)
				stmt.close();
				if (context != null)
					context.close();
				if ( conn != null )
					conn.close();
			} catch (NamingException|SQLException e1) {
				
				e1.printStackTrace();
			}


		}

	}
	
	private List<SendMessageToClient> SortByDate(List<SendMessageToClient> messagesList) throws ParseException
	{
		for(SendMessageToClient msg : messagesList){
			DateFormat dateFormat = new SimpleDateFormat(AppConstants.TimeFormat);
			Date MessageDate = dateFormat.parse(msg.getDateTime());
			DateTime msgDate = new DateTime(MessageDate);
			msg.setDTFormat(msgDate);
		}
		sort_by_date(messagesList);
		return messagesList;
	}
	
	public void sort_by_date(List<SendMessageToClient> messagesList) {
	    Comparator<SendMessageToClient> comparator;
	    comparator = dateComparator;
	    Collections.sort(messagesList, comparator);
	}
	
	
	Comparator<SendMessageToClient> dateComparator = new Comparator<SendMessageToClient>() {
	    @Override
	    public int compare(SendMessageToClient m1, SendMessageToClient m2) {
	        //return String.compare(m2.getDateTime(), m1.getDateTime());
	        return m2.getDTFormat().compareTo(m1.getDTFormat());
	    }
	};
	
	
	
	private String calcTime(String getTime) throws ParseException
	{
		String strTime="";
		DateFormat dateFormat = new SimpleDateFormat(AppConstants.TimeFormat);
		Date MessageDate = dateFormat.parse(getTime);
		DateTime msgDate = new DateTime(MessageDate);
		
		// get current time
		Date currtime = Calendar.getInstance().getTime();
		DateTime currentTime = new DateTime(currtime);
		
		int days = Days.daysBetween(msgDate,currentTime).getDays();
		int hours = Hours.hoursBetween(msgDate , currentTime).getHours();
		int minutes = Minutes.minutesBetween(msgDate , currentTime).getMinutes();
		int seconds= Seconds.secondsBetween( msgDate , currentTime).getSeconds();
		if (days >0)
		{
			strTime = msgDate.getDayOfMonth() +"/"+msgDate.getMonthOfYear()+"/"+msgDate.getYear()+" "+msgDate.getHourOfDay() +":"+msgDate.getMinuteOfHour();
		}
		else if (hours > 0 )
		{
			if (hours == 1){
				strTime= hours +" Hour ago";
			}else{
				strTime= hours +" Hours ago";
			}
		}
		else if (minutes > 0 )
		{
			if (minutes == 1){
				strTime= minutes +" Minute ago";
			}else{
				strTime= minutes +" Minutes ago";
			}
		}
		else if (seconds > 0 )
		{
			if (seconds == 1){
				strTime= seconds +" Second ago";
			}else{
				strTime= seconds +" Seconds ago";
			}
		
		}
		else
		{
			strTime= "Posted Now";
		}

		return strTime;
	}
	
	/*
	 * Check if Username is follow Follower
	 * true- follow
	 * else false 
	 *  
	 */
	public static boolean CheckIfWeFollowed(String Username,String Follower) throws SQLException, NamingException 
	{
		Context context = null;
		BasicDataSource ds = null;
		Connection conn = null;
		Statement stmt= null ;
		boolean returnvalue = false;
		try {
			context = new InitialContext();
			ds = (BasicDataSource)context.lookup(AppConstants.DB_DATASOURCE);
			conn = ds.getConnection();
			//create Customers table
			stmt = conn.createStatement();
			
			String[] dt = AppConstants.CHECK_FOLLOW_SELECT.split("//");
			String exec = dt[0] + Username + dt[1] + Follower + dt[2];
			ResultSet res = stmt.executeQuery(exec);
			if (res.next()){
			String UserFromDB= res.getString(AppConstants.USER_ID_WHO_FOLLOW);
			String FollowerFromDB= res.getString(AppConstants.USER_ID_FOLLOWER);
			if( UserFromDB.equals(Username) && FollowerFromDB.equals(Follower) )
				returnvalue = true;
			}

			stmt.close();
			context.close();
			conn.close();
			return returnvalue;
		
		} catch (NamingException|SQLException e) {
			if (stmt != null)
				stmt.close();
			if (context != null)
				context.close();
			if ( conn != null)
				conn.close();
			
			return returnvalue;
		}
		
		
		
	}

	
	private List<SendMessageToClient> getCommentsOfUser(String nickname )
	{
		Context context = null;
		BasicDataSource ds = null;
		Connection conn = null;
		Statement stmt = null;
		List<SendMessageToClient> temp = new ArrayList<SendMessageToClient>();
		try 
		{
			context = new InitialContext();
			ds = (BasicDataSource)context.lookup(AppConstants.DB_DATASOURCE);
			conn = ds.getConnection();
			//create Customers table
			stmt = conn.createStatement();	
			String exec = AppConstants.GET_COMMENTS_SELECT_OF_ME.replaceAll("//", nickname);
			ResultSet res = stmt.executeQuery(exec);
			

			while (res.next() )
			{
				String id = res.getString(AppConstants.COMMENTS_ID);
				String Nickname = nickname;
				String Username = GetUserNameFromNickname(nickname);
				String Text = res.getString(AppConstants.COMMENTS_TEXT);
				Text = AbstractGet.chkUsersTopics(Text);
				String Time = res.getString(AppConstants.COMMENTS_TIME);
				String DateTime = Time;
				Time = calcTime(Time);
				SendMessageToClient msg = new SendMessageToClient(Integer.parseInt(id),Username,Text,Time,"false",Nickname,"false",0,DateTime);
				temp.add(msg);
			}
					


			//CommentsResult.clear();
			conn.commit();
			stmt.close();
			conn.close();
			context.close();
			
			
		} 
		catch (NamingException |SQLException |ParseException  e) {
			
			e.printStackTrace();

			try {
				if (stmt != null)
				stmt.close();
				if (context != null)
					context.close();
				if ( conn != null )
					conn.close();
			} catch (NamingException|SQLException e1) {
				
				e1.printStackTrace();
			}


		}
		return temp;
	}
	
	
	
	
	private int getCommentsNumber(int msgId )
	{
		int Counter = 0;
		Context context = null;
		BasicDataSource ds = null;
		Connection conn = null;
		Statement stmt = null;

		try 
		{
			context = new InitialContext();
			ds = (BasicDataSource)context.lookup(AppConstants.DB_DATASOURCE);
			conn = ds.getConnection();
			//create Customers table
			stmt = conn.createStatement();			
			String[] dt = AppConstants.GET_COMMENTS_SELECT.split("//");
			String exec = dt[0] +msgId +dt[1];
			ResultSet res = stmt.executeQuery(exec);


			while (res.next() )
			{
				Counter++;
			}


			//CommentsResult.clear();
			conn.commit();
			stmt.close();
			conn.close();
			context.close();
			
			
		} 
		catch (NamingException |SQLException  e) {
			
			e.printStackTrace();

			try {
				if (stmt != null)
				stmt.close();
				if (context != null)
					context.close();
				if ( conn != null )
					conn.close();
			} catch (NamingException|SQLException e1) {
				
				e1.printStackTrace();
			}


		}
		return Counter;
	}
	
	private Collection<SendMessageToClient> SortByPopularity( Collection<SendMessageToClient> IncomingArray ) throws SQLException, NamingException, ParseException
	{
		Collection<SendMessageToClient> SortedArray = new ArrayList<SendMessageToClient>(); // the Sorted array
		HashMap<Integer, Double>  map = new HashMap<Integer, Double>();
		int IncomingArraySize = IncomingArray.size();
		
		Iterator<SendMessageToClient> t = IncomingArray.iterator();
		// for each message calculate the popularity
		for(int i=0; i < IncomingArraySize ;i++)
		{
			//move to next msg
			SendMessageToClient Msg = (SendMessageToClient) t.next();

			// Part 1 - find how much followers has - to the user that wrote the message
			int NumOfFollowers = GetNumberOfFollowers(Msg.getUsername());
			// Part 2 - find how much the message was republished
			int NumOfRepulish = GetNumberOfRepulishedMessage(Msg,AppConstants.MESSAGE_TYPE_REPUBLISH);
			double popularity =  Math.log(2+NumOfFollowers) +  Math.log(2+NumOfRepulish);
			if (Msg.getCommentsNumber() > 0 ) // if the message has comments - we need to find the popularity from the comment
			{
				double tempPop =  GetCommentsMaxPopularity (Msg,AppConstants.MESSAGE_TYPE_REPUBLISH_COMMENT);
				Msg.setPopularity(Math.max(tempPop, popularity));
			}
			else
			{
				Msg.setPopularity( popularity );
			}
			map.put(i, Msg.getPopularity());
		}
		// Sort the messages by popularity
		ArrayList<Entry<Integer, Double>> ListToSort = new ArrayList<>();
		ListToSort.addAll(map.entrySet() );
		Collections.sort(ListToSort, new EntryComparator());
		
		Object[] IncomingList = IncomingArray.toArray();
		
		// need to set at the final Sorted Array
		for (int i=0 ; i < AppConstants.MAX_MESSAGES ;i++)
		{
			if ( i == IncomingArray.size())
				break;
			SortedArray.add( (SendMessageToClient) IncomingList[ ListToSort.get(i).getKey()  ] );
		}
		
		return SortedArray;
	}
	
	public int GetNumberOfFollowers(String User) throws SQLException, NamingException
	{
		int NumOfFollowers = 0;
		Context context = null;
		BasicDataSource ds = null;
		Connection conn = null;
		Statement stmt = null;
		
		try{
			context = new InitialContext();
			ds = (BasicDataSource)context.lookup(AppConstants.DB_DATASOURCE);
			conn = ds.getConnection();
			//create Customers table
			stmt = conn.createStatement();
			String exec= AppConstants.CHECK_NUMBER_OF_FOLLOWERS.replaceFirst("//",  User );
			ResultSet res = stmt.executeQuery(exec);
			if ( res.next())
				NumOfFollowers = Integer.parseInt( res.getString(1) );
			
			
			//Release
			res.close();
			conn.commit();
			stmt.close();
			conn.close();
			context.close();
		}
		catch (NamingException |SQLException  e) {

				if (stmt != null)
					stmt.close();
				if (context != null)
					context.close();
				if ( conn != null )
					conn.close();
				NumOfFollowers = 0;
		}
		return NumOfFollowers;
	}
	
	public int GetNumberOfRepulishedMessage( SendMessageToClient Msg,String Type) throws SQLException, NamingException
	{
		int NumOfRepulish = 0;
		Context context = null;
		BasicDataSource ds = null;
		Connection conn = null;
		Statement stmt = null;
		
		try{
			context = new InitialContext();
			ds = (BasicDataSource)context.lookup(AppConstants.DB_DATASOURCE);
			conn = ds.getConnection();
			//create Customers table
			stmt = conn.createStatement();
			String exec= AppConstants.CHECK_NUMBER_OF_REPUBLISH.replaceFirst("//", Type );
			exec = exec.replaceFirst("//", String.valueOf( Msg.getId() ) );
			ResultSet res = stmt.executeQuery(exec);
			if ( res.next())
				NumOfRepulish = Integer.parseInt( res.getString(1) );
			
			
			//Release
			res.close();
			conn.commit();
			stmt.close();
			conn.close();
			context.close();
		}
		catch (NamingException |SQLException  e) {

				if (stmt != null)
					stmt.close();
				if (context != null)
					context.close();
				if ( conn != null )
					conn.close();
				NumOfRepulish = 0;
		}
		return NumOfRepulish;
	}

	public double GetCommentsMaxPopularity(SendMessageToClient Msg,String Type) throws ParseException, SQLException, NamingException
	{
		double popularity =0;
		Context context = null;
		BasicDataSource ds = null;
		Connection conn = null;
		Statement stmt = null;
		try{
			context = new InitialContext();
			ds = (BasicDataSource)context.lookup(AppConstants.DB_DATASOURCE);
			conn = ds.getConnection();
			//create Customers table
			stmt = conn.createStatement();
			
			String exec= AppConstants.GET_COMMENTS_SELECT.replace("//", String.valueOf(Msg.getId()));
			
			ResultSet res = stmt.executeQuery(exec);
			
			while (res.next() )
			{
				String id = res.getString(AppConstants.COMMENTS_ID);
				//String msgid= res.getString(AppConstants.COMMENTS_MESSAGE_ID);
				String Nickname= res.getString(AppConstants.COMMENTS_WHO_WROTE);
				SendMessageToClient tempComment = new SendMessageToClient(Integer.parseInt(id), "", "", "", "", Nickname, "", 0, "");
				
				//Get username from nickname -because the function    NumOfFollowers   uses Username as parameter
				String CommentUsername = GetUserNameFromNickname(Nickname);
				// need to calc popularity
				int NumOfFollowers = GetNumberOfFollowers(CommentUsername);
				int NumOfRepulish = GetNumberOfRepulishedMessage(tempComment,Type);
				double tempPop = Math.log(2+NumOfFollowers) +  Math.log(2+NumOfRepulish);
				if ( tempPop >popularity )
					popularity = tempPop;
    			
			}
			
			
			
			//Release
			res.close();
			conn.commit();
			stmt.close();
			conn.close();
			context.close();
		}
		catch (NamingException |SQLException  e) {

				if (stmt != null)
					stmt.close();
				if (context != null)
					context.close();
				if ( conn != null )
					conn.close();
				return popularity;

		}
		
		
		return popularity;
	}
	
	public String GetUserNameFromNickname(String nick) throws SQLException, NamingException{
		String username=null;
		Context context = null;
		BasicDataSource ds = null;
		Connection conn = null;
		Statement stmt = null;
		try{
			context = new InitialContext();
			ds = (BasicDataSource)context.lookup(AppConstants.DB_DATASOURCE);
			conn = ds.getConnection();
			//create Customers table
			stmt = conn.createStatement();
			
			String exec= AppConstants.USERS_SELECT_FIND_USER_FROM_NICK.replace("//", nick);
			
			ResultSet res = stmt.executeQuery(exec);
			if( res.next())
				username = res.getString(AppConstants.USERNAME);
			//Release
			res.close();
			conn.commit();
			stmt.close();
			conn.close();
			context.close();
		}
		catch (NamingException |SQLException  e) {

				if (stmt != null)
					stmt.close();
				if (context != null)
					context.close();
				if ( conn != null )
					conn.close();

		}

		return username;
	}
	
	class EntryComparator implements Comparator<Entry<Integer, Double>> {

	    public int compare(Entry<Integer, Double> arg0, Entry<Integer, Double> arg1) {
		// Compare the values.
		return arg1.getValue().compareTo(arg0.getValue());
	    }
	}

}
